﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#include "opengl.h"

#ifdef _UXS_USE_OPENGL

#include "../OS/Base/OSTypes.h"

#pragma comment(lib, "OpenGL32.lib")


namespace UX_STL
{
namespace UXTypesExt
{
/*
=================================================
	очистить стэк ошибок
=================================================
*/
	void TAPIErrorChecker::ClearGLErrors()
	{
		for (uint i = 0; i < _GetMaxErrors() and glGetError() != GL_NO_ERROR; ++i) {}
	}
	
/*
=================================================
	проверить не было ли ошибки
=================================================
*/
	bool TAPIErrorChecker::CheckGLError(uni_c_string pGLFunc, uni_c_string pFunc, uni_c_string pFile, uint uLine)
	{
		GLenum	err = glGetError();
		string	str( "OpenGL error: " );

		if ( err == GL_NO_ERROR )
			return true;

		switch ( err )
		{
			case GL_INVALID_ENUM:
				str << "GL_INVALID_ENUM";
				break;

			case GL_INVALID_VALUE:
				str << "GL_INVALID_VALUE";
				break;

			case GL_INVALID_OPERATION:
				str << "GL_INVALID_OPERATION";
				break;

			case GL_INVALID_FRAMEBUFFER_OPERATION:
				str << "GL_INVALID_FRAMEBUFFER_OPERATION";
				break;

			case GL_OUT_OF_MEMORY:
				str << "GL_OUT_OF_MEMORY";
				break;

			default:
				str << "code 0x" << string().FormatI( err, 16 );
				break;
		}

		str << ", in " << pGLFunc;
		str << ", function: " << pFunc;

		LOG_EXT( str.cstr(), e_log_msg::ERROR_2, pFile.cstr(), uLine );
		return false;
	}
	
/*
=================================================
	проверить не было ли ошибок
=================================================
*/
	bool TAPIErrorChecker::CheckGLErrors(uni_c_string pFile, uint uLine)
	{
		GLenum	err;
		bool	ret = true;

		for (uint i = 0; i < _GetMaxErrors() and (err = glGetError()) != GL_NO_ERROR; ++i)
		{
			string	str( "OpenGL error: " );

			switch ( err )
			{
				case GL_INVALID_ENUM:
					str << "GL_INVALID_ENUM";
					break;

				case GL_INVALID_VALUE:
					str << "GL_INVALID_VALUE";
					break;

				case GL_INVALID_OPERATION:
					str << "GL_INVALID_OPERATION";
					break;

				case GL_INVALID_FRAMEBUFFER_OPERATION:
					str << "GL_INVALID_FRAMEBUFFER_OPERATION";
					break;

				case GL_OUT_OF_MEMORY:
					str << "GL_OUT_OF_MEMORY";
					break;

				default:
					str << "code 0x" << string().FormatI( err, 16 );
					break;
			}

			LOG_EXT( str.cstr(), e_log_msg::ERROR_2, pFile.cstr(), uLine );
			ret = false;
		}

		return ret;
	}

}	// UXTypesExt
}	// UX_STL




#ifdef UX_GL_DEBUG_LOG
#	if UX_GL_DEBUG_LOG == 1 or UX_GL_DEBUG_LOG == 3
#		define _GL_LOG_EXTENSION( _log )	_log
#	else
#		define _GL_LOG_EXTENSION( _log )
#	endif
#	if UX_GL_DEBUG_LOG >= 2
#		define _GL_LOG_PROCADDRESS( _log )	_log
#	else
#		define _GL_LOG_PROCADDRESS( _log )
#	endif
#endif

extern "C"
{


#ifdef PLATFORM_WINDOWS
#	define GL_GETPROCADDRESS( _address )	UX_STL::winapi::wglGetProcAddress( (UX_STL::winapi::LPCSTR)_address )
#endif

#ifdef PLATFORM_LINUX
#	define GL_GETPROCADDRESS( _address )	glXGetProcAddress( (const GLubyte *)_address )
#endif

#ifndef GL_GETPROCADDRESS
#	error not defined "*GetProcAddress" function!
#endif


// получить адрес функции из драйвера
#define OPENGL_GET_PROC( p, n, d ) \
	if ( (__##n = (p)GL_GETPROCADDRESS( #n ))		== null ) \
	if ( (__##n = (p)GL_GETPROCADDRESS( #n"ARB" ))	== null ) \
	if ( (__##n = (p)GL_GETPROCADDRESS( #n"EXT" ))	== null ) \
	if ( (__##n = (p)GL_GETPROCADDRESS( #n"OES" ))	== null ) \
	if ( (__##n = (p)GL_GETPROCADDRESS( #n"ATI" ))	== null ) \
	if ( (__##n = (p)GL_GETPROCADDRESS( #n"AMD" ))	== null ) \
	if ( (__##n = (p)GL_GETPROCADDRESS( #n"NV"  ))	== null ) \
	{ \
		__##n = d; \
		_GL_LOG_PROCADDRESS( _aLog.PushBack( "Can't get address of function \""#n"\"." ); ) \
		ret &= GL_FALSE; \
	}


// проверить поддерживается ли расширение
#define OPENGL_GET_EXTENSION( n ) \
	__##n = _IsExtSupported( #n ); \
	if ( __##n != GL_TRUE ) { \
		_GL_LOG_EXTENSION( _aLog.PushBack( "Not supported extension \""#n"\"." ); ) \
		ret &= GL_FALSE; \
	}



#ifdef UX_GL_USE_DEFAULT_FUNC

	// Default Functions //
	#undef  UX_GL_BUILDFUNC
	#define UX_GL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
		GLAPI _retType CALLBACK glDefault##_funcName _funcParams \
		{ \
			WARNING( "used default function: gl"#_funcName ); \
			return _retValue; \
		} \
	
	UX_GL_FUNCTIONS

	#undef  UX_GL_BUILDFUNC
	#define UX_GL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue )

#endif	// UX_GL_USE_DEFAULT_FUNC
	
}	// extern "C"
	


TGLContext::TGLContext(): _pMinAddress(null), _pMaxAddress(null)
{
}


const TGLContext::string_array_t & TGLContext::GetLog()	const
{
	return _aLog;
}


void TGLContext::ClearLog()
{
	_aLog.Clear();
}


bool TGLContext::_IsExtSupported(const char *pExtName)
{
	if ( pExtName == null )
		return false;

	GLint			i_num_ext	= 0;
	const GLchar  *	p_ext		= null;

	glGetIntegerv( GL_NUM_EXTENSIONS, &i_num_ext );
	
	for (int i = 0; i < i_num_ext; ++i)
	{
		p_ext = (const GLchar *)__glGetStringi( GL_EXTENSIONS, i );

		if ( p_ext == null )
			break;

		if ( ::_hidden_::strcmp( p_ext, pExtName ) == 0 )
			return true;
	}

	return false;
}


bool TGLContext::IsValid(void * const pFunc) const
{
	return !( pFunc == null or ( pFunc >= _pMinAddress and pFunc <= _pMaxAddress ) );
}


bool TGLContext::Init()
{
	GLboolean	ret = GL_TRUE;
	
	
	// FUNCTIONS //
	#undef UX_GL_BUILDFUNC
	#ifdef UX_GL_USE_DEFAULT_FUNC
		#define UX_GL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			OPENGL_GET_PROC( PFNGL##_funcName##PROC,	gl##_funcName,	&glDefault##_funcName ); \
			if ( &glDefault##_funcName < _pMinAddress ) _pMinAddress = &glDefault##_funcName; \
			if ( &glDefault##_funcName > _pMaxAddress )	_pMaxAddress = &glDefault##_funcName;
	#else
		#define UX_GL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			OPENGL_GET_PROC( PFNGL##_funcName##PROC,	gl##_funcName,	null );
	#endif	// UX_GL_USE_DEFAULT_FUNC
	
	UX_GL_FUNCTIONS

	#undef  UX_GL_BUILDFUNC
	#define UX_GL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue )



	// EXTENSIONS //
	#undef  UX_GL_BUILDVAR
	#define UX_GL_BUILDVAR( _valType, _valName ) \
		OPENGL_GET_EXTENSION( GL_##_valName );
	
	UX_GL_EXTENSIONS

	#undef  UX_GL_BUILDVAR
	#define UX_GL_BUILDVAR( _valType, _valName )


	// CONSTANTS //
	#undef  UX_GL_BUILDVAR
	#define UX_GL_BUILDVAR( _valType, _valName ) \
		glGetIntegerv( GL_##_valName, &__GL_##_valName );
	
	UX_GL_CONSTANTS_I
	
	#undef  UX_GL_BUILDVAR
	#define UX_GL_BUILDVAR( _valType, _valName ) \
		glGetFloatv( GL_##_valName, &__GL_##_valName );

	UX_GL_CONSTANTS_F

	#undef  UX_GL_BUILDVAR
	#define UX_GL_BUILDVAR( _valType, _valName )

	return ret == GL_TRUE;
}



void TGLContext::Clear()
{
	
	// FUNCTIONS //
	#undef UX_GL_BUILDFUNC
	#ifdef UX_GL_USE_DEFAULT_FUNC
		#define UX_GL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			__gl##_funcName = &glDefault##_funcName;
	#else
		#define UX_GL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			__gl##_funcName = null;
	#endif	// UX_GL_USE_DEFAULT_FUNC
	
	UX_GL_FUNCTIONS

	#undef  UX_GL_BUILDFUNC
	#define UX_GL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue )



	// EXTENSIONS //
	#undef  UX_GL_BUILDVAR
	#define UX_GL_BUILDVAR( _valType, _valName ) \
		__GL_##_valName = GL_FALSE;
	
	UX_GL_EXTENSIONS

	#undef  UX_GL_BUILDVAR
	#define UX_GL_BUILDVAR( _valType, _valName )


	// CONSTANTS //
	#undef  UX_GL_BUILDVAR
	#define UX_GL_BUILDVAR( _valType, _valName ) \
		__GL_##_valName = 0;
	
	UX_GL_CONSTANTS_I

	#undef  UX_GL_BUILDVAR
	#define UX_GL_BUILDVAR( _valType, _valName )
}


#undef	GL_GETPROCADDRESS
#undef	OPENGL_GET_EXTENSION
#undef	OPENGL_GET_PROC
#undef	_GL_LOG_EXTENSION
#undef	_GL_LOG_PROCADDRESS


#endif	// _UXS_USE_OPENGL